# -*- coding: utf-8 -*-
"""
@deprecated 仅保留入口，全部代理到 AlarmService
"""
from __future__ import annotations

from common.database import DatabaseManager
from common.logger import create_logger
from common.event_bus import get_event_bus
from mqtt.consumer.domain.service import AlarmService
from mqtt.consumer.domain.strategy import ThresholdBackToNormalStrategy, AutoCloseOnRecoveredStrategy
from mqtt.consumer.event_models import QualityEvent
from mqtt.consumer.repository.alarm_repository import AlarmRepository
from mqtt.consumer.repository.work_order_repository import WorkOrderRepository
from mqtt.consumer.rule_engine import RuleEngine, Hit

logger = create_logger("legacy.alarm_worker", "INFO")


class AlarmWorker:
	"""代理到 domain.AlarmService"""

	def __init__(self, db: DatabaseManager, engine: RuleEngine):
		self.db = db
		self.engine = engine
		self.service = AlarmService(
			db=db,
			recovery_strategy=ThresholdBackToNormalStrategy(),
			close_strategy=AutoCloseOnRecoveredStrategy(),
		)
		self.alarm_repo = AlarmRepository(db)
		self.work_order_repo = WorkOrderRepository(db)
		# 移除outbox_repo，使用事件总线替代
		# self.outbox_repo = OutboxRepository(db)

	# ------------- 唯一入口 -------------
	def on_event(self, event: QualityEvent) -> None:
		logger.info(f"[AlarmWorker] 接收事件: meter_code={event.meter_code}, quality={event.quality}, msg_id={event.msg_id}")

		if event.quality == "GOOD":
			logger.info(f"[AlarmWorker] 处理GOOD质量事件，检查告警恢复: {event.meter_code}")
			# 步骤1: 数据库事务处理告警恢复和工单关闭
			with self.db.transaction():
				# 检查是否有告警需要恢复
				recovered = self.service.recover_if_needed(event)
				if not recovered:
					logger.debug(f"[AlarmWorker] 无需恢复告警: {event.meter_code}")
					return

				logger.info(f"[AlarmWorker] 告警已恢复: alarm_id={recovered.alarm_id}, alarm_type={recovered.payload.get('alarm_type')}")

				# 自动关闭工单
				closed = self.service.close_work_order_if_needed(recovered.alarm_id)
				if closed:
					logger.info(f"[AlarmWorker] 工单已关闭: work_order_id={closed.payload.get('work_order_id')}, alarm_id={closed.alarm_id}")

			# 步骤2: 事务提交后，发布事件到内存队列（异步推送）
			try:
				event_bus = get_event_bus()
				# 发布告警恢复事件
				event_bus.publish(recovered.event_type, recovered.payload)
				logger.info(f"[AlarmWorker] 恢复事件已发布: alarm_id={recovered.alarm_id}")

				# 如果工单已关闭，也发布工单关闭事件（可选）
				if closed:
					event_bus.publish(closed.event_type, closed.payload)
					logger.debug(f"[AlarmWorker] 工单关闭事件已发布: alarm_id={closed.alarm_id}")
			except Exception as e:
				logger.error(f"[AlarmWorker] 事件发布失败: {e}", exc_info=True)
				# 事件发布失败不影响数据库事务，前端可通过轮询获取

			return

		# BAD -> 告警
		logger.info(f"[AlarmWorker] 处理BAD质量事件: {event.meter_code}")
		alarm_type = self._top_alarm_type(event)
		if not alarm_type:
			return

		# 检测到新告警类型
		logger.info(f"[DIAG-AlarmWorker] 新告警: {alarm_type}, {event.meter_code}")

		# **简化架构：告警创建 + 工单创建 in 单一事务**
		with self.db.transaction():
			# 检查是否已存在同类型告警
			if self.alarm_repo.check_existing_alarm(event.msg_id, alarm_type):
				logger.info("[SKIP] 告警已存在 msg_id=%s alarm_type=%s", event.msg_id, alarm_type)
				return

			# 步骤1: 创建新告警
			raised_event = self.service.raise_alarm(event, alarm_type)
			logger.info(f"[AlarmWorker] 告警创建成功: alarm_id={raised_event.alarm_id}")

			# 步骤2: 创建工单（同一事务内）
			existing_work_order = self.work_order_repo.find_work_order_by_alarm(raised_event.alarm_id)
			if existing_work_order:
				logger.info("[SKIP] 告警已有关联工单 alarm_id=%s wo_id=%s", raised_event.alarm_id, existing_work_order['id'])
			else:
				try:
					# 创建工单和映射关系
					wo_id = self._insert_work_order(event, raised_event.alarm_id)
					self.alarm_repo.insert_alarm_work_order(raised_event.alarm_id, wo_id)
					logger.info("[CREATE] 工单已创建 wo_id=%s alarm_id=%s", wo_id, raised_event.alarm_id)
				except Exception as e:
					if "Duplicate entry" in str(e):
						logger.info("[SKIP] 工单创建冲突 alarm_id=%s", raised_event.alarm_id)
					else:
						raise

		logger.info(f"[AlarmWorker] 事务已提交: alarm_id={raised_event.alarm_id}")

		# 步骤3: 事务外发布事件到内存队列（异步推送，不影响数据库事务）
		try:
			event_bus = get_event_bus()
			event_bus.publish(raised_event.event_type, raised_event.payload)
			logger.info(f"[AlarmWorker] 告警事件已发布: alarm_id={raised_event.alarm_id}, type={raised_event.event_type}")
		except Exception as e:
			logger.error(f"[AlarmWorker] 事件发布失败: {e}", exc_info=True)
			# 事件发布失败不影响数据库事务，前端可通过轮询获取

	# ------------- 辅助 -------------
	def _top_alarm_type(self, event: QualityEvent) -> str | None:
		hits: list[Hit] = []
		hits += self.engine.evaluate_row_fields(event, event.msg_id, event.meter_type)
		hits += self.engine.evaluate_snapshot_events(event.raw_snapshot, event.msg_id, event.meter_type)
		if not hits:
			return None
		hits.sort(key=lambda h: h.severity, reverse=True)
		return hits[0].rule_code

	def _build_detailed_description(self, event: QualityEvent, alarm_type: str, rule_name: str = '') -> str:
		"""
		构造详细的工单描述，包含完整的异常消息体

		Args:
			event: 质量事件
			alarm_type: 告警类型
			rule_name: 规则名称（可选）

		Returns:
			str: 详细的描述信息
		"""
		try:
			# 基本信息
			alarm_display_name = rule_name if rule_name and rule_name.strip() else self._get_alarm_display_name(alarm_type)

			description_parts = [
				f"电表{event.meter_code}发生{alarm_display_name}"
			]

			# 添加具体的测量值信息
			measurement_info = self._format_measurement_info(event, alarm_type)
			if measurement_info:
				description_parts.append(measurement_info)

			# 添加完整的快照数据
			snapshot_info = self._format_snapshot_info(event.raw_snapshot)
			if snapshot_info:
				description_parts.append(f"当前数据快照：{snapshot_info}")

			# 添加处理建议
			handling_advice = self._get_handling_advice(alarm_type)
			if handling_advice:
				description_parts.append(handling_advice)

			# 添加最后的通用描述
			description_parts.append("请及时处理，详见告警快照和现场排查。")

			return "，".join(description_parts)

		except Exception as e:
			logger.error(f"构造详细描述失败: {e}")
			# 降级为简单描述
			return f"电表{event.meter_code}发生异常，请及时处理。详见告警快照和现场排查。"

	def _get_alarm_display_name(self, alarm_type: str) -> str:
		"""获取告警类型的显示名称"""
		try:
			from common.alarm_utils import get_alarm_type_label
			return get_alarm_type_label(alarm_type)
		except Exception:
			return alarm_type

	def _format_measurement_info(self, event: QualityEvent, alarm_type: str) -> str:
		"""格式化测量值信息"""
		try:
			info_parts = []

			# 根据告警类型显示相关的测量值
			if alarm_type in ["TEMP_OVER", "HIGH_TEMP"]:
				if hasattr(event, 'temperature1') and event.temperature1 is not None:
					info_parts.append(f"T1={event.temperature1}°C")
				if hasattr(event, 'temperature2') and event.temperature2 is not None:
					info_parts.append(f"T2={event.temperature2}°C")
			elif alarm_type in ["OVER_VOLTAGE", "VOLTAGE_OVER", "LOW_VOLTAGE", "UNDER_VOLTAGE"]:
				if hasattr(event, 'voltage') and event.voltage is not None:
					info_parts.append(f"电压={event.voltage}V")
			elif alarm_type in ["OVER_CURRENT", "CURRENT_OVER"]:
				if hasattr(event, 'current') and event.current is not None:
					info_parts.append(f"电流={event.current}A")
			elif alarm_type in ["OVER_POWER", "POWER_OVER", "OVER_LOAD"]:
				if hasattr(event, 'watt') and event.watt is not None:
					info_parts.append(f"功率={event.watt}W")

			if info_parts:
				return f"异常数值：{', '.join(info_parts)}"
			return ""

		except Exception as e:
			logger.debug(f"格式化测量值信息失败: {e}")
			return ""

	def _format_snapshot_info(self, raw_snapshot: dict) -> str:
		"""格式化快照数据"""
		try:
			if not raw_snapshot:
				return ""

			snapshot_parts = []

			# 按照重要性排序显示快照数据
			key_mappings = {
				'voltage': '电压',
				'current': '电流',
				'watt': '功率',
				'temperature1': 'T1',
				'temperature2': 'T2',
				'power_factor': '功率因数',
				'frequency': '频率'
			}

			for key, label in key_mappings.items():
				if key in raw_snapshot and raw_snapshot[key] is not None:
					value = raw_snapshot[key]
					unit = self._get_unit_for_key(key)
					snapshot_parts.append(f"{label}={value}{unit}")

			return ", ".join(snapshot_parts)

		except Exception as e:
			logger.debug(f"格式化快照信息失败: {e}")
			return ""

	def _get_unit_for_key(self, key: str) -> str:
		"""获取测量值的单位"""
		unit_map = {
			'voltage': 'V',
			'current': 'A',
			'watt': 'W',
			'temperature1': '°C',
			'temperature2': '°C',
			'power_factor': '',
			'frequency': 'Hz'
		}
		return unit_map.get(key, '')

	def _get_handling_advice(self, alarm_type: str) -> str:
		"""获取处理建议"""
		advice_map = {
			"TEMP_OVER": "建议检查环境温度和设备散热情况",
			"HIGH_TEMP": "建议检查环境温度和设备散热情况",
			"OVER_VOLTAGE": "建议检查电网电压稳定性和调压设备",
			"VOLTAGE_OVER": "建议检查电网电压稳定性和调压设备",
			"OVER_CURRENT": "建议检查负载情况和线路连接",
			"CURRENT_OVER": "建议检查负载情况和线路连接",
			"OVER_LOAD": "建议检查负载分配和用电设备",
			"COMM_LOST": "建议检查通信线路和设备连接",
			"TAMPER": "建议检查设备安全和防护装置",
			"THREE-TAMPER": "建议检查设备安全、封签和防护装置",
			"SINGLE-TAMPER": "建议检查设备安全、封签和防护装置"
		}
		return advice_map.get(alarm_type, "")

	def _insert_work_order(self, event: QualityEvent, alarm_id: int) -> int:
		"""
		创建工单，使用优化的标题和描述格式
		"""
		code = self._gen_wo_code()

		# 从数据库获取告警详情，包括rule_name
		try:
			alarm_info = self.alarm_repo.get_alarm_info_by_id(alarm_id)
			if alarm_info:
				alarm_type = alarm_info.get('alarm_type', 'UNKNOWN_ALARM')
				rule_name = alarm_info.get('rule_name', '')
			else:
				# 如果获取不到告警信息，使用推断的告警类型
				alarm_type = self._top_alarm_type(event) or "UNKNOWN_ALARM"
				rule_name = ''
		except Exception as e:
			logger.warning(f"获取告警信息失败: {e}，使用推断的告警类型")
			alarm_type = self._top_alarm_type(event) or "UNKNOWN_ALARM"
			rule_name = ''

		# 优先使用rule_name作为标题，否则使用格式化的中文标签
		if rule_name and rule_name.strip():
			title = f"电表{event.meter_code}{rule_name}"
		else:
			from common.alarm_utils import format_work_order_title
			title = format_work_order_title(event.meter_code, alarm_type)

		# 构造详细的描述信息，包含完整的异常消息体
		description = self._build_detailed_description(event, alarm_type, rule_name)

		logger.info(f"创建工单: {title}")
		logger.debug(f"工单描述: {description}")

		return self.work_order_repo.insert_work_order(
			code,
			title,
			alarm_id,
			description
		)

	def _gen_wo_code(self) -> str:
		from datetime import datetime
		today = datetime.now().strftime("%Y%m%d")
		key = f"wo:seq:{today}"
		import common.redis
		r = common.redis.get_redis_service()
		if r.is_available():
			seq = r.incr(key)
			if seq == 1:
				r.expire(key, 86400)
			return f"W{today}{seq:04d}"
		# 降级 DB
		row = self.db.execute_query(
			"""
			SELECT CONCAT('W', DATE_FORMAT(NOW(),'%Y%m%d'), LPAD(COALESCE(MAX(RIGHT(code,4)),0)+1, 4, '0')) AS code
			FROM wo_work_order
			WHERE code LIKE CONCAT('W', DATE_FORMAT(NOW(),'%Y%m%d'), '%')
			FOR UPDATE
			""",
			commit=False,
		)[0]
		return row["code"]
